<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:t="http://kadm.com.ua/XMLSchema/Text"
    xmlns="http://kadm.com.ua/XMLSchema/Grammar">

    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template match="@*" />
    <xsl:template match="text()" />

    <xsl:template match="/grammar">
        <t:file>
            <t:l>require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'sets'.DIRECTORY_SEPARATOR.'set.php';</t:l>
            <t:l>require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'parser.php';</t:l>
            <t:l></t:l>
            <t:l>class <xsl:value-of select="@name" /> extends Parser</t:l>
            <t:l>{</t:l>
            <t:l></t:l>
            <t:block>
                <t:l>function __construct()</t:l>
                <t:l>{</t:l>
                <t:block>
                    <t:l>parent::__construct();</t:l>
                    <t:l>$this->main = "<xsl:value-of select="rule[1]/@NT" />";</t:l>
                </t:block>
                <t:l>}</t:l>
                <xsl:apply-templates select="rule" />
            </t:block>
            <t:l></t:l>
            <t:l>}</t:l>
        </t:file>
    </xsl:template>

    <xsl:template match="rule">
        <t:l></t:l>
        <t:l>function NT_<xsl:value-of select="@NT" />()</t:l>
        <t:l>{</t:l>
        <t:block>
            <t:l>$this->createChild('<xsl:value-of select="@NT" />');</t:l>
            <t:l></t:l>
            <xsl:apply-templates select="*" />
            <t:l></t:l>
            <t:l>$this->endChild();</t:l>
        </t:block>
        <t:l>}</t:l>
    </xsl:template>

    <!-- Механизмы выбора -->

    <xsl:template match="*[@use = 'optional']">
        <t:l>if ($this->IsNotEnd() &amp;&amp; <xsl:apply-templates select="." mode="first-cond" />) {</t:l>
        <t:block>
            <xsl:apply-templates select="." mode="parse" />
        </t:block>
        <t:l>}</t:l>
    </xsl:template>

    <xsl:template match="*[@use = 'multi']">
        <t:l>while ($this->IsNotEnd() &amp;&amp; <xsl:apply-templates select="." mode="first-cond" />) {</t:l>
        <t:block>
            <xsl:apply-templates select="." mode="parse" />
        </t:block>
        <t:l>}</t:l>
    </xsl:template>

    <xsl:template match="*[@use = 'more']">
        <t:l>do {</t:l>
        <t:block>
            <xsl:apply-templates select="." mode="parse" />
        </t:block>
        <t:l>} while ($this->IsNotEnd() &amp;&amp; <xsl:apply-templates select="." mode="first-cond" />);</t:l>
    </xsl:template>

    <xsl:template match="*">
        <t:l>if (! $this->IsNotEnd()) {</t:l>
        <t:block>
            <t:l>$this->Error();</t:l>
        </t:block>
        <t:l>}</t:l>
        <xsl:apply-templates select="." mode="parse" />
    </xsl:template>

    <!-- Непосредственно механизмы распознавания -->

    <xsl:template match="choose" mode="parse">
        <t:l>switch (true) {</t:l>
        <xsl:for-each select="*">
            <t:l></t:l>
            <t:l>case <xsl:apply-templates select="." mode="first-cond" />:</t:l>
            <t:block>
                <xsl:apply-templates select="." />
                <t:l>break;</t:l>
            </t:block>
        </xsl:for-each>
        <t:l></t:l>
        <t:l>default:</t:l>
        <t:block>
            <t:l>$this->Error();</t:l>
        </t:block>
        <t:l>}</t:l>
    </xsl:template>

    <xsl:template match="sequence" mode="parse">
        <xsl:apply-templates select="*" />
    </xsl:template>

    <xsl:template match="NT" mode="parse">
        <t:l>$this->NT_<xsl:value-of select="text()" />();</t:l>
    </xsl:template>

    <xsl:template match="literal" mode="parse">
        <t:l>foreach (str('<xsl:value-of select="translate(translate(text(), '\', '\\'), &quot;'&quot;, &quot;\'&quot;)" />') as $char) {</t:l>
        <t:block>
            <t:l>if ($this->Ch() != $char) {</t:l>
            <t:block>
                <t:l>$this->Error();</t:l>
            </t:block>
            <t:l>}</t:l>
            <t:l>$this->NextCh();</t:l>
            <t:l>if (! $this->IsNotEnd()) {</t:l>
            <t:block>
                <t:l>$this->Error();</t:l>
            </t:block>
            <t:l>}</t:l>
        </t:block>
        <t:l>}</t:l>
    </xsl:template>

    <xsl:template match="class | not-class" mode="parse">
        <t:l>
            <xsl:text>$set = new UnionSet(</xsl:text>
            <xsl:for-each select="*">
                <xsl:if test="position() > 1">
                    <xsl:text>, </xsl:text>
                </xsl:if>
                <xsl:apply-templates select="." />
            </xsl:for-each>
            <xsl:text>);</xsl:text>
        </t:l>
        <xsl:variable name="not-mode">
            <xsl:if test="name() = 'class'">
                <xsl:text>! </xsl:text>
            </xsl:if>
        </xsl:variable>
        <t:l>if (<xsl:value-of select="$not-mode" />$set->has_a(str::getCode($this->Ch()))) {</t:l>
        <t:block>
            <t:l>$this->Error();</t:l>
        </t:block>
        <t:l>}</t:l>
        <t:l>$this->NextCh();</t:l>
    </xsl:template>

    <xsl:template match="range">
        <xsl:text>new RangeSet(</xsl:text>
        <xsl:apply-templates select="*[1]" mode="integer" />
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="*[2]" mode="integer" />
        <xsl:text>)</xsl:text>
    </xsl:template>

    <xsl:template match="char" mode="integer">
        <xsl:text>str::getCode('</xsl:text>
        <xsl:value-of select="translate(translate(text(), '\', '\\'), &quot;'&quot;, &quot;\'&quot;)" />
        <xsl:text>')</xsl:text>
    </xsl:template>

    <xsl:template match="code" mode="integer">
        <xsl:value-of select="text()" />
    </xsl:template>

    <xsl:template match="hex-code" mode="integer">
        <xsl:text>0x</xsl:text>
        <xsl:value-of select="text()" />
    </xsl:template>

    <xsl:template match="hex-code | code | char">
        <xsl:text>new SingleSet(</xsl:text>
        <xsl:apply-templates select="." mode="integer" />
        <xsl:text>)</xsl:text>
    </xsl:template>

    <!-- First conditions templates -->

    <xsl:template match="sequence" mode="first">
        <xsl:apply-templates select="*[1]" mode="first-in-sequence" />
    </xsl:template>

    <xsl:template match="*" mode="first-in-sequence">
        <xsl:apply-templates select="." mode="first" />
        <xsl:if test="@use = 'optional' or @use = 'multi'">
            <xsl:text>, </xsl:text>
            <xsl:apply-templates select="following-sibling::*" mode="first-in-sequence" />
        </xsl:if>
    </xsl:template>

    <xsl:template match="*" mode="first-cond">
        <xsl:text>Set(</xsl:text>
        <xsl:apply-templates select="." mode="first" />
        <xsl:text>)->has_a(str::getCode($this->Ch()))</xsl:text>
    </xsl:template>

    <!-- First templates -->

    <xsl:template match="NT" mode="first">
        <xsl:variable name="NT" select="text()" />
        <xsl:apply-templates select="//rule[@NT = $NT]" mode="first" />
    </xsl:template>

    <xsl:template match="literal" mode="first">
        <xsl:text>new SingleSet(str::getCode('</xsl:text>
        <xsl:value-of select="translate(translate(substring(text(),1,1), '\', '\\'), &quot;'&quot;, &quot;\'&quot;)" />
        <xsl:text>'))</xsl:text>
    </xsl:template>

    <xsl:template match="choose" mode="first">
        <xsl:text>new UnionSet(</xsl:text>
        <xsl:for-each select="*">
            <xsl:if test="position() > 1">
                <xsl:text>, </xsl:text>
            </xsl:if>
            <xsl:apply-templates select="." mode="first" />
        </xsl:for-each>
        <xsl:text>)</xsl:text>
    </xsl:template>

    <xsl:template match="class | not-class" mode="first">
        <xsl:text>new UnionSet(</xsl:text>
        <xsl:for-each select="*">
            <xsl:if test="position() > 1">
                <xsl:text>, </xsl:text>
            </xsl:if>
            <xsl:apply-templates select="." />
        </xsl:for-each>
        <xsl:text>)</xsl:text>
    </xsl:template>

    <xsl:template match="range | hex-code | code | char" mode="first">
        <xsl:apply-templates select="." />
    </xsl:template>

</xsl:stylesheet>